package com.example.run_game;

import java.text.DecimalFormat;
import java.util.Date;
import java.util.Random;

import android.location.Location;
import android.util.Log;

public class SubObjective
{
	public enum sotype
	{
		VISITCHECKPOINTONCE,
		VISITCHECKPOINTTWICE,
		BEATSPEED,
		BEATPERSONALBEST,
		HOLDSPEEDFORTIME,
		BURNCALORIES,
		BEATSPEEDUPHILL		
	}	
	
	public String desc;
	public int xpValue;
	public sotype type;
	public boolean achieved = false;
	
	private Checkpoint goal;
	private double speed;
	private String personalBest;
	private int time;
	private long timeGate =0;
	private int calories;
	private int counter = 0;
	private boolean startedTime = false;
	
	private Random rand;
	private DbManager db;
	private Checkpoint[] ckps;
	private Player player;
	
	public SubObjective(DbManager db, Checkpoint[] ckps, Player player, int typeInt)
	{
		rand = new Random();
		this.db = db;
		this.ckps = ckps;
		this.player = player;
		this.type = sotype.values()[typeInt];
		
		switch(type)
		{
		case VISITCHECKPOINTONCE: 
			setupVisitCheckpointNotChoice();
			break;
		case VISITCHECKPOINTTWICE: 
			setupVisitCheckpointTwice();
			break;
		case BEATSPEED:
			setupBeatSpeed();
			break;
		case BEATPERSONALBEST :
			setupBeatPersonalBest();
			break;
		case HOLDSPEEDFORTIME:
			setupHoldSpeedForTime();
			break;
		case BURNCALORIES:
			setupBurnCalories();
			break;
		case BEATSPEEDUPHILL:
			setupBeatSpeedUphill();
			break;
		}
	}

	private int adjustment(int size)
	{
		int sign = rand.nextInt(10);
		if(sign > 5)
			sign = -1;
		else 
			sign = 1;
		return rand.nextInt(size) * 100 * sign;
	}
	
	private Checkpoint getRandomCheckpoint()
	{
	  int index = rand.nextInt(ckps.length);
	  return ckps[index];
	}
	
	private void setupBeatSpeed()
	{
		 type = sotype.BEATSPEED;
		 speed = rand.nextDouble() * 2 + 2;
		 DecimalFormat nf = new DecimalFormat("#.#");		 
		 desc = "Break the speed of " + nf.format(speed) + "m/s";
		 if(speed > 3)
			 xpValue = 600;
		 else
			 xpValue = 200;	
	}
	private void setupVisitCheckpointTwice()
	{
		 type = sotype.VISITCHECKPOINTTWICE;
		 goal = getRandomCheckpoint();
		 desc = "Visit "+ goal.name +" twice";
		 xpValue = 800 + adjustment(2);
	}

	private void setupVisitCheckpointNotChoice()
	{
		 type = sotype.VISITCHECKPOINTONCE;
		 goal = getRandomCheckpoint();
		 desc = "Visit "+goal.name;
		 xpValue = 300 + adjustment(1);	
	}
	
	private void setupBeatPersonalBest()
	{
		type = sotype.BEATPERSONALBEST;
		String[] possibles = {"longest run", "furthest run", "fastest run"};
		personalBest = possibles[rand.nextInt(3)];
		desc = "Beat your current " + personalBest;
		xpValue = 700 + adjustment(2);	
	}
	
	private void setupHoldSpeedForTime()
	{
		 type = sotype.HOLDSPEEDFORTIME;
		 speed = rand.nextDouble() + 2.4;
		 DecimalFormat nf = new DecimalFormat("#.#");
		 int timeMinutes = rand.nextInt(5) +1 ;
		 time = timeMinutes * 60;		 
		 desc = "Hold the speed of " + nf.format(speed) + "m/s for "+timeMinutes+ " minutes";
		 if(speed > 3)
			 xpValue = 600;
		 else
			 xpValue = 400;	
		 if(timeMinutes > 3)
			 xpValue += 300;	 			
	}
	
	private void setupBeatSpeedUphill()
	{
		 type = sotype.BEATSPEEDUPHILL;
		 type = sotype.BEATSPEED;
		 speed = rand.nextDouble() + 2.2;
		 DecimalFormat nf = new DecimalFormat("#.#");		 
		 desc = "Break the speed of " + nf.format(speed) + "m/s whilst going uphill";
		 if(speed > 2.7)
			 xpValue = 600;
		 else
			 xpValue = 200+adjustment(1);				
	}

	private void setupBurnCalories()
	{
		 type = sotype.BURNCALORIES;
		 int adjustment = adjustment(2);
		 calories = 300 + adjustment;
		 desc = "Burn " + calories + " calories";
		 xpValue = 400 + adjustment;	
	}
	
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public boolean isAchieved(Location loc, Location previousLoc, double distance, long time, double speed, int calories)
	{
		if(achieved)
			return false;
		switch(type)
		{
		case VISITCHECKPOINTONCE: 
			return testVisitCheckpointOnce(loc);			
		case VISITCHECKPOINTTWICE: 
			return testVisitCheckpointTwice(loc);			
		case BEATSPEED:
			return testBeatSpeed(loc, previousLoc);			
		case BEATPERSONALBEST :
			return testBeatPersonalBest(distance, time, speed);			
		case HOLDSPEEDFORTIME:
			return testHoldSpeedForTime(speed, time);			
		case BURNCALORIES:
			return testBurnCalories(calories);			
		case BEATSPEEDUPHILL:
			return testBeatSpeedUphill(loc, previousLoc, speed);			
		}
		return false;		
	}

	private boolean testBeatSpeed(Location loc, Location previousLoc)
	{
		double distance = (int) loc.distanceTo(previousLoc);
		long timePassed = loc.getTime() - previousLoc.getTime();
	    if(timePassed == 0)
	    	return false;
		double currentspeed = distance * 1000 / timePassed;
		if(currentspeed > speed)	
			achieved = true;
		return achieved;	
	}

	private boolean testVisitCheckpointOnce(Location loc)
	{
	    if(loc.distanceTo(goal.location) < goal.radius)
	    	achieved = true;
	    return achieved;
	}

	private boolean testVisitCheckpointTwice(Location loc)
	{
	    if(loc.distanceTo(goal.location) < goal.radius)
	    	counter++;
	    if(counter >= 2)
	    	achieved = true;
	    return achieved;
	}

	private boolean testBeatPersonalBest(double distance, long time2, double speed2)
	{
		if(personalBest.equals("longest run") && time2 > player.getLongestRun() && player.getTotalRuns() >= 5)
		{
			achieved = true;
		}
		else if(personalBest.equals("furthest run") && distance > player.getFurthestRun() && player.getTotalRuns() >= 5)
		{
			achieved = true;
		}
		else if(personalBest.equals("fastest run") && speed2 > player.getBestSpeed() && player.getTotalRuns() >= 5)
		{
			achieved = true;
		}
		return achieved;
	}

	private boolean testHoldSpeedForTime(double speed2, long time2)
	{
		if(startedTime)
		{
			if( timeGate + time*1000 < new Date().getTime())
			{
				startedTime = false;
				if(speed2 > speed)
					achieved = true;
			}				
		}
		else if(speed2 > speed)
		{
			timeGate = new Date().getTime();
			startedTime = true;
		}
		if(speed2 < speed)		
			startedTime = false;		
		return false;
	}

	private boolean testBurnCalories(int calories2)
	{
		if( calories2 > calories)
			achieved = true;
		return achieved;
	}

	private boolean testBeatSpeedUphill(Location loc, Location previousLoc,
			double speed2)
	{
		double distance = (int) loc.distanceTo(previousLoc);
		long timePassed = loc.getTime() - previousLoc.getTime();
		double currentspeed = distance * 1000 / timePassed;
		if(currentspeed > speed && loc.getAltitude() > previousLoc.getAltitude())	
			achieved = true;
		return achieved;
	}
	
}
